﻿/*----------------------------------------------------------------
//  <copyright file="FunctionAuthorizationHandler.cs" company="MicroCloud@151504200868">
//      Copyright © 2020-2024 MicroCloud Corporation, All rights reserved.
//  </copyright>
//  <site>https://gitee.com/chenmm123/microclouds</site>
//  <last-editor>cmm</last-editor>
//  <last-date>2024-03-04 14:46</last-date>
//----------------------------------------------------------------*/

using MicroCloud.AspNetCore.Mvc.Filters;
using MicroCloud.Audits;
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Security;

namespace MicroCloud.Authorization
{
    #region "功能授权处理器"
    /// <summary>
    /// 功能授权处理器
    /// </summary>
    public class FunctionAuthorizationHandler : AuthorizationHandler<AuthorizationRequirement>
    {
        //字段
        private readonly IFunctionAuthorization _functionAuthorization;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IServiceProvider _provider;

        #region "构造函数"
        #region "初始化一个功能授权处理器的新实例"
        /// <summary>
        /// 初始化一个功能授权处理器 <see cref="FunctionAuthorizationHandler"/> 的新实例
        /// </summary>
        /// <param name="functionAuthorization"></param>
        /// <param name="httpContextAccessor"></param>
        /// <param name="provider"></param>
        public FunctionAuthorizationHandler(IFunctionAuthorization functionAuthorization, IHttpContextAccessor httpContextAccessor, IServiceProvider provider)
        {
            _functionAuthorization = functionAuthorization;
            _httpContextAccessor = httpContextAccessor;
            _provider = provider;
        }
        #endregion

        #endregion

        #region "方法"
        #region "如果根据特定要求允许授权，则作出决定"
        /// <summary>
        /// 如果根据特定要求允许授权，则作出决定。
        /// </summary>
        /// <param name="context">The authorization context.</param>
        /// <param name="requirement">The requirement to evaluate.</param>
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizationRequirement requirement)
        {
            RouteEndpoint endpoint = null;
            HttpContext httpContext = null;
            switch (context.Resource)
            {
                case HttpContext resource1:
                    httpContext = resource1;
                    endpoint = httpContext.GetEndpoint() as RouteEndpoint;
                    break;
                case RouteEndpoint resource2:
                    endpoint = resource2;
                    httpContext = _httpContextAccessor.HttpContext;
                    break;
            }

            if (endpoint == null || httpContext == null)
            {
                return Task.CompletedTask;
            }

            var controllerActionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
            var methodInfo = controllerActionDescriptor.MethodInfo;
            var controllerTypeInfo = controllerActionDescriptor.ControllerTypeInfo;
            //是否具有操作升级特性声明
            var isAuditOperation = methodInfo.IsDefined(typeof(AuditOperationAttribute), true) || controllerTypeInfo.IsDefined(typeof(AuditOperationAttribute), true);

            IFunction function = endpoint.GetExecuteFunction(httpContext);
            if (function != null)
            {
                AuthorizationResult result = AuthorizeCore(context, function);
                if (result.IsOk)
                {
                    context.Succeed(requirement);
                }
                else
                {
                    if (function.AuditOperationEnabled)
                    {
                        AuditOperationEntry operation = new()
                        {
                            FunctionName = function.Name,
                            Ip = httpContext.GetClientIp(),
                            UserAgent = httpContext.Request.UserAgent(),
                            Method = httpContext.Request.Method,
                            ContentType = httpContext.Request.ContentType,
                            QueryString = httpContext.Request.QueryString.ToUriComponent(),
                            Arguments = httpContext.Request.BodyArguments(true),
                            CreatedTime = DateTime.Now
                        };
                        if (httpContext.User.Identity.IsAuthenticated && httpContext.User.Identity is ClaimsIdentity identity)
                        {
                            operation.UserId = identity.GetUserId();
                            //operation.UserName = identity.GetUserName();
                            //operation.NickName = identity.GetNickName();
                        }
                        operation.ResultType = result.ResultType.CastTo(AjaxResultType.Error);
                        operation.Message = result.Message;
                        operation.ResultData = result.Data?.ToJsonString();

                        _provider.BeginUnitOfWorkTransaction(scopeProvider =>
                        {
                            operation.EndedTime = DateTime.Now;
                            IAuditHandler store = scopeProvider.GetService<IAuditHandler>();
                            store?.Save(operation);

                        });
                    }

                }
            }
            else
            {
                //不存在功能信息时，分别验证在method和controller中的特定标记属性，角色权限由于无功能信息，视同登录授权处理。
                var isLoggedIn = methodInfo.IsDefined(typeof(LoggedInAttribute), true) || controllerTypeInfo.IsDefined(typeof(LoggedInAttribute), true);
                var isRoleLimit = methodInfo.IsDefined(typeof(RoleLimitAttribute), true) || controllerTypeInfo.IsDefined(typeof(RoleLimitAttribute), true);
                if (isLoggedIn || isRoleLimit)
                {
                    ClaimsPrincipal user = context.User;
                    if (user?.Identity?.IsAuthenticated == true)
                    {
                        context.Succeed(requirement);
                    }
                }
            }

            return Task.CompletedTask;
        }
        #endregion
        #region "重写以实现功能权限的核心验证逻辑"
        /// <summary>
        /// 重写以实现功能权限的核心验证逻辑
        /// </summary>
        /// <param name="context">权限过滤器上下文</param>
        /// <param name="function">要验证的功能</param>
        /// <returns>权限验证结果</returns>
        protected virtual AuthorizationResult AuthorizeCore(AuthorizationHandlerContext context, IFunction function)
        {
            ClaimsPrincipal user = context.User;
            AuthorizationResult result = _functionAuthorization.Authorize(function, user);
            return result;
        }
        #endregion

        #endregion

    }
    #endregion

}
